vis.Network.prototype.animateTraffic = function(edgesTrafficList, onPreAnimationHandler, onPreAnimateFrameHandler, onPostAnimateFrameHandler, onPostAnimationHandler) {
  this.trafficAnimator = {
    offset: 0,
    thisNetwork: this,
    trafficCanvas: null,
    trafficCanvasCtx: null,
    trafficCanvasWidth: null,
    trafficCanvasHeight: null,
    reportedErrors: {},
    edgesTrafficList: edgesTrafficList,
    onPreAnimateFrame: onPreAnimateFrameHandler,
    ontPostAnimateFrame: onPostAnimateFrameHandler,
    onPreAnimation: onPreAnimationHandler,
    onPostAnimation: onPostAnimationHandler,
    animationProcess: true,
    parseEdgeTraffic(edgeTraffic) {
      let edge = this.thisNetwork.body.edges[edgeTraffic.edge]

      return {
        edge: edge,
        trafficSize: edgeTraffic.trafficSize || 1,
        animateColor: edgeTraffic.animateColor,
        nodeSize: edgeTraffic.nodeSize,
        isBackward: edge && edgeTraffic.isBackward
      }
    },

    clearAnimationCanvas() {
      this.trafficCanvasCtx.save()
      this.trafficCanvasCtx.setTransform(1, 0, 0, 1, 0, 0)
      this.trafficCanvasCtx.clearRect(0, 0, this.trafficCanvasWidth, this.trafficCanvasHeight)
      this.trafficCanvasCtx.restore()
    },

    getNetworkTrafficCanvas() {
      this.trafficCanvas = this.thisNetwork.body
        .container.getElementsByClassName('networkTrafficCanvas')[0]

      let frame = this.thisNetwork.canvas.frame
      this.trafficCanvas = document.createElement('canvas')
      this.trafficCanvas.className = 'networkTrafficCanvas'
      this.trafficCanvas.style.position = 'absolute'
      this.trafficCanvas.style.top = this.trafficCanvas.style.left = 0
      this.trafficCanvas.style.zIndex = 1
      this.trafficCanvas.style.pointerEvents = 'none'
      this.trafficCanvas.style.width = frame.style.width
      this.trafficCanvas.style.height = frame.style.height

      this.trafficCanvas.width = frame.canvas.clientWidth
      this.trafficCanvas.height = frame.canvas.clientHeight

      frame.appendChild(this.trafficCanvas)

      return this.trafficCanvas
    },

    animateFrame() {
      let maxOffset = 1
      if (this.offset > maxOffset) {
        if (this.onPostAnimation) {this.onPostAnimation(this.edgesTrafficList)}
        return
      }
      for (let i in this.edgesTrafficList) {
        let edgeTraffic = this.parseEdgeTraffic(this.edgesTrafficList[i])
        if (!edgeTraffic.edge) {
          return
        }

        if (this.onPreAnimateFrame && this.onPreAnimateFrame(edgeTraffic) === false) {
          continue
        }
        const r = edgeTraffic.nodeSize

        // 若边没有设置长度，则获取两点直线距离
        const {x: x1, y: y1} = edgeTraffic.edge.edgeType.getPoint(0)
        const {x: x2, y: y2} = edgeTraffic.edge.edgeType.getPoint(1)

        if (!x1 || !y1 || !x2 || !y2) {
          return
        }

        const edge1 = Math.abs(x1 - x2)
        const edge2 = Math.abs(y1 - y2)
        const edgeLenth = Math.sqrt(edge1 * edge1 + edge2 * edge2)

        // 因为边从节点圆心出发，获取节点半径占边的比例，设置动画起始位置
        const curOffset = this.offset + r / (edgeTraffic.edge.options.length || edgeLenth)
        let p = edgeTraffic.edge.edgeType.getPoint(Math.min(1, curOffset))

        this.trafficCanvasCtx.beginPath()
        let grad = this.trafficCanvasCtx.createRadialGradient(p.x, p.y, 1, p.x, p.y, 18)

        grad.addColorStop(0, Color(edgeTraffic.animateColor || edgeTraffic.edge.options.color.color).alpha(1).string())
        this.trafficCanvasCtx.fillStyle = grad
        this.trafficCanvasCtx.arc(p.x, p.y, parseInt(edgeTraffic.trafficSize) || 1, 0, Math.PI * 2, false)
        this.trafficCanvasCtx.fill()
        // this.trafficCanvasCtx.stroke()
        this.trafficCanvasCtx.closePath()

        if (this.onPostAnimateFrame && this.onPostAnimateFrame(edgeTraffic) === false) {
          if (this.onPostAnimation) {
            this.onPostAnimation(this.edgesTrafficList)
          }
          return
        }
      }
      this.offset += 0.005
    },
    draw() {
      this.animateFrame()
      this.trafficCanvasCtx.save()
      this.trafficCanvasCtx.globalCompositeOperation = 'destination-in'
      this.trafficCanvasCtx.fillStyle = 'rgba(0, 0, 0, .95)'
      this.trafficCanvasCtx.fillRect(-this.trafficCanvasWidth, -this.trafficCanvasHeight, this.trafficCanvasWidth * 2, this.trafficCanvasHeight * 2)
      // this.trafficCanvasCtx.fill()
      this.trafficCanvasCtx.restore()
      if (this.offset > 1) {
        this.offset = 0
      }
      if (this.animationProcess) {
        this.raf = requestAnimationFrame(this.draw.bind(this))
      }
    },

    initalizeCanvasForEdgeAnimation() {
      this.reportedErrors = {}
      this.trafficCanvas = this.getNetworkTrafficCanvas()
      this.trafficCanvasCtx = this.trafficCanvas.getContext('2d')
      this.trafficCanvasWidth = this.trafficCanvasCtx.canvas.width
      this.trafficCanvasHeight = this.trafficCanvasCtx.canvas.height

      let s = this.thisNetwork.getScale()// edgeTraffic.edge.body.view.scale
      let t = this.thisNetwork.body.view.translation

      this.trafficCanvasCtx.setTransform(1, 0, 0, 1, 0, 0)
      this.trafficCanvasCtx.translate(t.x, t.y)
      this.trafficCanvasCtx.scale(s, s)
    }
  }

  this.trafficAnimator.initalizeCanvasForEdgeAnimation()

  if (this.trafficAnimator.onPreAnimation &&
    this.trafficAnimator.onPreAnimation(this.trafficAnimator.edgesTrafficList) === false) {
    return
  }
  this.trafficAnimator.draw()
}
vis.Network.prototype.animateTrafficClear = function() {
  if (this.trafficAnimator && this.trafficAnimator.trafficCanvas) {
    this.trafficAnimator.clearAnimationCanvas()
    cancelAnimationFrame(this.raf)
    this.trafficAnimator.animationProcess = false
    this.canvas.frame.removeChild(this.trafficAnimator.trafficCanvas)
    this.trafficAnimator.trafficCanvas = null
  }
}
vis.Network.prototype.gradient = function(startColor, endColor, step) {
  // 将hex转换为rgb
  const sColor = Color(startColor).color
  const eColor = Color(endColor).color

  // 计算R\G\B每一步的差值
  const rStep = (eColor[0] - sColor[0]) / step
  const gStep = (eColor[1] - sColor[1]) / step
  const bStep = (eColor[2] - sColor[2]) / step

  const gradientColorArr = [...Array(step)].map((item, index) => {
    return Color(`rgb(${parseInt(rStep * index + sColor[0])}, ${parseInt(gStep * index + sColor[1])}, ${parseInt(bStep * index + sColor[2])})`).hex()
  })
  return gradientColorArr
}